﻿using System.Collections;
using System;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Data;

namespace CCM.Framework
{
    /// <summary>
    /// Un ObjectsList est une Hashtable qui est structuré comme dans l'exemple suivant :
    /// HashTable 
    /// |_ (Key -> Object.GetType(), Value -> new Hashtable())
    ///                                                 |_ (Key -> Object.Key, Value -> new RepositoryObject(Object.Key, service, mode))
    ///                                                 |_ (Key -> Object.Key, Value -> new RepositoryObject(Object.Key, service, mode))
    /// |_ (Key -> Object.GetType(), Value -> new Hashtable())
    ///                                                 |_ (Key -> Object.Key, Value -> new RepositoryObject(Object.Key, service, mode))
    ///                                                 |_ (Key -> Object.Key, Value -> new RepositoryObject(Object.Key, service, mode))
    ///
    /// MyHashTable 
    /// |_ (Key -> "Utilisateur_DTO, Value -> new Hashtable())
    ///                                                 |_ (Key -> "DLIND", Value -> new RepositoryObject("DLIND", GestionUtilisateurServicePage, Consultation))
    ///                                                 |_ (Key -> "FBERTON", Value -> new RepositoryObject("FBERTON", GestionUtilisateurServicePage, Saisie))
    /// |_ (Key -> Commune_DTO, Value -> new Hashtable())
    ///                                                 |_ (Key -> "DLIND", Value -> new RepositoryObject("DLIND", CommuneServicePage, Consultation))
    ///                                                 |_ (Key -> "FBERTON", Value -> new RepositoryObject("FBERTON", DepartementServicePage, Consultation))
    ///                        
    /// 
    /// L'ObjectsRepository sert également à gérer le property changed sur les objets qui passe en mode Edit
    /// </summary>
    public abstract class ObjectsRepository
    {
        //private static ObjectsRepository m_ObjectsRepository = null;
        public const string DEFAULT_KEY_FOR_CREATE = "CREATE_NEW_OBJECT";

        protected ObjectsRepository()
        {
            // Cette classe est un singleton, utilisez CreateObjectsList()
            //_ObjectsRepository = new Hashtable();
            _ObjectsRepository = new SortedDictionary<string, SortedDictionary<string, RepositoryObject>>();
        }

        /// <summary>
        /// Récèpère la clé de l'objet par introspection
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <param name="type">Le type de l'objet</param>
        /// <returns></returns>
        private string GetKeyValue(object obj, Type type)
        {
            //Les objets doivent tous contenir une property nommé Key qui contient une clé unique
            //représentant l'objet. On récupère donc cette clé par introspection de l'objet.
            string keyValue = null;
            PropertyInfo pi = type.GetProperty("Key");
            if (pi != null)
            {
                object key = pi.GetValue(obj, null);
                if (key != null)
                {
                    keyValue = key.ToString();
                }
                else
                {
                    //On suppose que si l'objet n'a pas de clef alors c'est qu'il est en cours de création.
                    //Pour que le répository puisse l'identifier on retourne la clef par défaut qui a été associé à 
                    //l'objet en création.
                    keyValue = DEFAULT_KEY_FOR_CREATE;
                }
                return keyValue;
            }
            else
            {
                throw new Exception("Dans GetKeyValue : L'objet n'a pas de Key property. Il n'est pas conforme pour l'ObjectsRepository");
            }
        }

        /// <summary>
        /// Récupère le SortedDictionary correspondant au type de l'objet.
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <param name="keyValue">la clef de l'objet retourné en out</param>
        /// <returns>Le dictionnaire correspondant et la cle de l'objet mis dans le param out</returns>
        private SortedDictionary<string,RepositoryObject> GetObjectsRepositoryTyped(object obj, out string keyValue)
        {
            //on récupère le type de l'objet
            Type type = obj.GetType();
            keyValue = GetKeyValue(obj, type);
            if (_ObjectsRepository.ContainsKey(type.Name) == true)
            {
                return _ObjectsRepository[type.Name] as SortedDictionary<string, RepositoryObject>;
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Permet de savoir si un objet est présent dans le ObjectsRepository
        /// </summary>
        /// <param name="obj">L'objet à tester</param>
        /// <returns>Vrai si il est présent, faux dans le cas contraire</returns>
        public bool ObjectExistInRepository(object obj)
        {
            if (obj == null) return false;
            //on récupère le type de l'objet
            Type type = obj.GetType();
            string keyValue = GetKeyValue(obj, type);

            if (_ObjectsRepository.ContainsKey(type.Name) == true)
            {
                SortedDictionary<string, RepositoryObject> dico = _ObjectsRepository[type.Name] as SortedDictionary<string, RepositoryObject>;
                if (dico.ContainsKey(keyValue) == true) return true;
            }
            return false;
        }

        /// <summary>
        /// Ajoute un objet dans la collection. 
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="service"></param>
        /// <param name="mode"></param>
        /// <param name="overrideMode"></param>
        public bool AddObject(object obj, RepositoryObject.eMode mode)
        {
            //on récupère le type de l'objet
            Type type = obj.GetType();
            string keyValue = null;
            keyValue = GetKeyValue(obj, type);

            if (string.IsNullOrEmpty(keyValue)) throw new Exception("Dans AddObject : la clef de l'objet ne doit jamais être null ou vide");

            if (_ObjectsRepository.ContainsKey(type.Name) == false)
            {
                SortedDictionary<string, RepositoryObject> dico = new SortedDictionary<string, RepositoryObject>();
                dico.Add(keyValue, new RepositoryObject(obj, mode));
                _ObjectsRepository.Add(type.Name, dico);
                if (mode == RepositoryObject.eMode.Create || mode == RepositoryObject.eMode.Edit)
                {
                    SubscribeObjetToEventPropertyChanged(obj);
                }
                if (ObjectsRepositoryChanged != null)
                    ObjectsRepositoryChanged(this, new CollectionChangeEventArgs(CollectionChangeAction.Add, obj));
                return true;
            }
            else
            {
                SortedDictionary<string, RepositoryObject> dico = _ObjectsRepository[type.Name] as SortedDictionary<string, RepositoryObject>;
                if (dico.ContainsKey(keyValue) == false)
                {
                    dico.Add(keyValue, new RepositoryObject(obj, mode));
                    if (mode == RepositoryObject.eMode.Create || mode == RepositoryObject.eMode.Edit)
                    {
                        SubscribeObjetToEventPropertyChanged(obj);
                    }
                    if (ObjectsRepositoryChanged != null)
                        ObjectsRepositoryChanged(this, new CollectionChangeEventArgs(CollectionChangeAction.Add, obj));
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Permet de modifier la clef de l'objet dans la collection. Avant la création la clef est par défaut  : CREATE_OBJECT
        /// Il faut la modifier pour pouvoir accéder correctement au nouvel objet crée qui a désormais une vrai clef.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="newKey"></param>
        public void ChangeObjectKey(Type type, string oldKey, string newKey)
        {
            if (_ObjectsRepository.ContainsKey(type.Name) == true)
            {
                SortedDictionary<string, RepositoryObject> dico = _ObjectsRepository[type.Name] as SortedDictionary<string, RepositoryObject>;
                dico[newKey] = dico[oldKey];
                dico.Remove(oldKey);
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Permet de s'abonner à l'évènement PropertyChanged
        /// </summary>
        /// <param name="obj"></param>
        private void SubscribeObjetToEventPropertyChanged(object obj)
        {
            INotifyPropertyChanged theObjectNotified = obj as INotifyPropertyChanged;
            if (theObjectNotified != null)
                theObjectNotified.PropertyChanged += new PropertyChangedEventHandler(theObjectNotified_PropertyChanged);
        }

        /// <summary>
        /// Permet de se désabonner de l'évènement PropertyChanged
        /// </summary>
        /// <param name="obj"></param>
        private void UnsubscribeEventPropertyChangedFromObject(object obj)
        {
            INotifyPropertyChanged theObjectNotified = obj as INotifyPropertyChanged;
            if (theObjectNotified != null)
                theObjectNotified.PropertyChanged -= theObjectNotified_PropertyChanged;
        }

        /// <summary>
        /// Permet de mettre à IsDirty lorsqu'une property de l'objet est modifé
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void theObjectNotified_PropertyChanged(Object sender, PropertyChangedEventArgs e)
        {
            //Attention cette condition empêche de setter en boucle la propriété IsDirty
            if (e.PropertyName != "IsDirty")
            {
                Type type = sender.GetType();
                PropertyInfo pi = type.GetProperty("IsDirty");
                if (pi != null)
                    pi.SetValue(sender, true, null);
            }
        }

        /// <summary>
        /// Permet de modifier le mode dans lequel l'objet se trouve.
        /// </summary>
        /// <param name="obj">L'objet à modifier</param>
        /// <param name="newMode">Le nouveau mode dans lequel on souhaite faire passer l'objet</param>
        public void SetCurrentModeForObject(object obj, RepositoryObject.eMode newMode)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;

                if (repositoryObject.CurrentMode != newMode)
                {
                    if (newMode == RepositoryObject.eMode.Create || newMode == RepositoryObject.eMode.Edit)
                    {
                        SubscribeObjetToEventPropertyChanged(obj);
                    }
                    else if (newMode == RepositoryObject.eMode.Consultation)
                    {
                        UnsubscribeEventPropertyChangedFromObject(obj);
                    }
                    repositoryObject.CurrentMode = newMode;
                    if (ObjectsRepositoryChanged != null)
                        ObjectsRepositoryChanged(this, new CollectionChangeEventArgs(CollectionChangeAction.Refresh, obj));
                }
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Retourne le mode d'un objet.
        /// </summary>
        /// <param name="obj">L'objet dont on souhaite connaitre le mode</param>
        /// <returns>Un élément de l'énumération RepositoryObject.eMode</returns>
        public RepositoryObject.eMode GetCurrentModeForObject(object obj)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                return repositoryObject.CurrentMode;
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Retourne la liste des objets d'un service (principalement utilisé pour retrouvé les objets d'une liste de recherche.
        /// </summary>
        /// <param name="service"></param>
        /// <returns></returns>
        public List<object> GetObjectsInServicePage(IServicePage service)
        {
            List<object> lstObject = new List<object>();
            foreach (SortedDictionary<string, RepositoryObject> dico in _ObjectsRepository.Values)
            {
                foreach (RepositoryObject repositoryObject in dico.Values)
                {
                    if (repositoryObject.Services.Contains(service) == true)
                        lstObject.Add(repositoryObject.TheObject);
                }
            }
            return lstObject;
        }

        /// <summary>
        /// Retourne la liste des list où l'objet est présent.
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <returns></returns>
        public List<object> GetListsWhereObjectIsIn(object obj)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                return repositoryObject.Lists;
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Permet de synchronisé un tableau d'objet avec les objets présent dans le ObjectsRepository
        /// </summary>
        /// <typeparam name="T">Type des éléments du tableau</typeparam>
        /// <param name="objListGeneric">Tableau d'objet</param>
        /// <returns></returns>
        protected T[] SynchronizedObjects<T>(T[] objListGeneric)
        {
            if (objListGeneric == null) throw new Exception("Appel de SynchronizedObjects de ObjectsRepository avec une liste NULL");

            for (int i = 0; i < objListGeneric.Length; i++)
            {
                object obj = objListGeneric[i];
                if (ObjectExistInRepository(obj) == true)
                {
                    RepositoryObject repositoryObject = GetRepositoryObject(obj.GetType(), GetKeyValue(obj, obj.GetType()));
                    objListGeneric[i] = (T)repositoryObject.TheObject;

                    repositoryObject.Lists.Add(objListGeneric);
                }
                else
                {
                    if (AddObject(obj, RepositoryObject.eMode.Consultation) == true)
                    {
                        RepositoryObject repositoryObject = GetRepositoryObject(obj.GetType(), GetKeyValue(obj, obj.GetType()));
                        repositoryObject.Lists.Add(objListGeneric);
                    }
                }
            }

            return objListGeneric;
        }

        /// <summary>
        /// Permet de synchronisé un tableau d'objet avec les objets présent dans le ObjectsRepository
        /// </summary>
        /// <typeparam name="T">Type des éléments du tableau</typeparam>
        /// <param name="objListGeneric">Tableau d'objet</param>
        /// <returns></returns>
        protected void SynchronizedObjects<T>(T[] sourceList, ObservableCollection<T> destinationList, IServicePage service)
        {
            if (sourceList == null) throw new Exception("Appel de SynchronizedObjects de ObjectsRepository avec une liste NULL");

            //On vide la liste de destination
            destinationList.Clear();

            for (int i = 0; i < sourceList.Length; i++)
            {
                object obj = sourceList[i];
                if (ObjectExistInRepository(obj) == true)
                {
                    RepositoryObject repositoryObject = GetRepositoryObject(obj.GetType(), GetKeyValue(obj, obj.GetType()));
                    destinationList.Add((T)repositoryObject.TheObject);
                    repositoryObject.Lists.Add(destinationList);
                    repositoryObject.Services.Add(service);
                }
                else
                {
                    if (AddObject(obj, RepositoryObject.eMode.Consultation) == true)
                    {
                        RepositoryObject repositoryObject = GetRepositoryObject(obj.GetType(), GetKeyValue(obj, obj.GetType()));

                        destinationList.Add((T)repositoryObject.TheObject);
                        repositoryObject.Lists.Add(destinationList);
                        repositoryObject.Services.Add(service);
                    }
                }
            }
        }

        /// <summary>
        /// Permet de supprimer un objet de la collection en spécifiant le service qui le représentait.
        /// Si d'autres service représente ce même objet alors cet objet n'est pas supprimé de la collection.
        /// </summary>
        /// <param name="obj">L'objet à supprimer.</param>
        /// <param name="service">Le service qui ne représente plus l'objet.</param>
        public void RemoveServicePageFromObject(object obj, IServicePage service)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                repositoryObject.RemoveServicePage(service);
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Permet de supprimer un objet de la collection en spécifiant le service qui le représentait.
        /// Si d'autres service représente ce même objet alors cet objet n'est pas supprimé de la collection.
        /// </summary>
        /// <param name="obj">L'objet à supprimer.</param>
        /// <param name="service">Le service qui ne représente plus l'objet.</param>
        public void RemoveServicePageFromAllObject(ICollectionView lstObj, IServicePage service)
        {
            foreach (object obj in lstObj)
            {
                RemoveServicePageFromObject(obj, service);
            }
        }

        /// <summary>
        /// Permet de supprimer un objet de la collection.
        /// Attention à ce mode suppression !!
        /// </summary>
        /// <param name="obj">L'objet à supprimer.</param>
        public void RemoveObjectFromRepository(object obj)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                dico.Remove(keyValue);
                if (ObjectsRepositoryChanged != null)
                    ObjectsRepositoryChanged(this, new CollectionChangeEventArgs(CollectionChangeAction.Remove, obj));
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Permet d'ajouter un service dans la liste des services qui représentent l'objet.
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <param name="service">Le service à ajouter</param>
        public bool AddListServicePageForObject(object obj, IServicePage service)
        {   
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                return repositoryObject.AddListServicePage(service);
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Permet d'ajouter un service dans la liste des services qui représentent l'objet.
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <param name="service">Le service à ajouter</param>
        public bool AddDetailServicePageForObject(object obj, IServicePage service)
        {
            
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                return repositoryObject.AddDetailServicePage(service);
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }
        
        /// <summary>
        /// Permet de récupérer le premier service qui représente l'objet.
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <returns>Le service page qui le représente</returns>
        public IServicePage GetDetailServicePageForObject(object obj)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                return repositoryObject.FirstDetailService;
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }

        /// <summary>
        /// Retourne la liste de tous les services pages qui représente l'objet.
        /// </summary>
        /// <param name="obj">L'objet</param>
        /// <returns>Une liste de service page</returns>
        public List<IServicePage> GetAllServicePageForObject(object obj)
        {
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            if (dico.ContainsKey(keyValue) == true)
            {
                RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                return repositoryObject.Services;
            }
            else
            {
                throw new Exception("L'objet n'est pas dans l'ObjectsRepository !!");
            }
        }
        
        /// <summary>
        /// Permet de savoir si un objet est présent dans le ObjectsRepository et représenté par le service spécifié
        /// </summary>
        /// <param name="obj">L'objet à tester</param>
        /// <param name="service">Service</param>
        /// <returns>Vrai si il est présent, faux dans le cas contraire</returns>
        public bool ObjectExistInServicePage(object obj, IServicePage service)
        {
            if (ObjectExistInRepository(obj) == false) return false;
            
            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
            if (repositoryObject.Services.Contains(service) == false) return false;
            return true;
        }

        /// <summary>
        /// Permet de savoir si un objet est présent dans le ObjectsRepository et représenté par le service spécifié
        /// </summary>
        /// <param name="obj">L'objet à tester</param>
        /// <param name="service">Service</param>
        /// <returns>Vrai si il est présent, faux dans le cas contraire</returns>
        public bool ObjectExistInDetailServicePage(object obj)
        {
            if (ObjectExistInRepository(obj) == false) return false;

            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
            if (repositoryObject.FirstDetailService == null) return false;
            return true;
        }

        /// <summary>
        /// Permet de savoir si un objet est présent dans le ObjectsRepository et présent dans une liste
        /// </summary>
        /// <param name="obj">L'objet à tester</param>
        /// <param name="service">Service</param>
        /// <returns>Vrai si il est présent, faux dans le cas contraire</returns>
        public bool ObjectExistInList(object obj, IList list)
        {
            if (ObjectExistInRepository(obj) == false) return false;

            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
            if (repositoryObject.Lists.Contains(list) == false) return false;
            return true;
        }

        /// <summary>
        /// Permet de savoir si un objet est présent dans un autre service que celui spécifié
        /// </summary>
        /// <param name="obj">L'objet à tester</param>
        /// <param name="service">Service</param>
        /// <returns>Vrai si il est présent, faux dans le cas contraire</returns>
        public bool ObjectExistInOtherServicePage(object obj, IServicePage service)
        {
            if (ObjectExistInRepository(obj) == false) return false;

            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;

            if (repositoryObject.Services.Count == 0) return false;
            if (repositoryObject.Services.Count == 1 && repositoryObject.Services.Contains(service) == true) return false;

            return true;
        }

        /// <summary>
        /// Permet de savoir si un objet est présent dans un autre service que celui spécifié
        /// </summary>
        /// <param name="obj">L'objet à tester</param>
        /// <param name="list">La list</param>
        /// <returns>Vrai si il est présent, faux dans le cas contraire</returns>
        public bool ObjectExistInOtherList(object obj, IList list)
        {
            if (ObjectExistInRepository(obj) == false) return false;

            string keyValue = null;
            SortedDictionary<string, RepositoryObject> dico = GetObjectsRepositoryTyped(obj, out keyValue);
            RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;

            if (repositoryObject.Lists.Count == 0) return false;
            if (repositoryObject.Lists.Count == 1 && repositoryObject.Lists.Contains(list) == true) return false;

            return true;
        }

        /// <summary>
        /// Permet de récupérer un objet du répository
        /// </summary>
        /// <param name="type">Le type de l'objet</param>
        /// <param name="keyValue">La clef de l'objet recherché</param>
        /// <returns>L'objet</returns>
        private object GetObject(Type type, string keyValue)
        {
            object theObject = null;
            if (_ObjectsRepository.ContainsKey(type.Name) == true)
            {
                SortedDictionary<string, RepositoryObject> dico = _ObjectsRepository[type.Name] as SortedDictionary<string, RepositoryObject>;
                if (dico.ContainsKey(keyValue) == true)
                {
                    RepositoryObject repositoryObject = dico[keyValue] as RepositoryObject;
                    theObject = repositoryObject.TheObject;
                }
            }
            return theObject;
        }

        /// <summary>
        /// Permet de récupérer un objet du répository
        /// </summary>
        /// <param name="type">Le type de l'objet</param>
        /// <param name="keyValue">La clef de l'objet recherché</param>
        /// <returns>L'objet</returns>
        protected RepositoryObject GetRepositoryObject(Type type, string keyValue)
        {
            RepositoryObject repositoryObject = null;
            if (_ObjectsRepository.ContainsKey(type.Name) == true)
            {
                SortedDictionary<string, RepositoryObject> dico = _ObjectsRepository[type.Name] as SortedDictionary<string, RepositoryObject>;
                if (dico.ContainsKey(keyValue) == true)
                {
                    repositoryObject = dico[keyValue] as RepositoryObject;
                }
            }
            return repositoryObject;
        }

        /// <summary>
        /// Permet de synchroniser une liste générique d'objet.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objListGeneric"></param>
        public void SynchronizedObjects<T>(IList<T> objListGeneric, IServicePage service)
        {
            if (objListGeneric == null) throw new Exception("Appel de SynchronizedObjects de ObjectsRepository avec une liste NULL");
            
            Type typeListGeneric = objListGeneric.GetType();
            if (typeListGeneric.IsGenericType == false) throw new Exception("Appel de SynchronizedObjects de ObjectsRepository avec une liste non générique");

            Type[] typeGen = typeListGeneric.GetGenericArguments();
            Type theTypeGen = typeGen[0];

            for (int i = 0; i < objListGeneric.Count; i++)
            {
                object obj = objListGeneric[i];
                if (ObjectExistInRepository(obj) == true)
                {
                    obj = GetObject(theTypeGen, GetKeyValue(obj, theTypeGen));
                }
            }
        }

        #region private fields
        protected SortedDictionary<string, SortedDictionary<string, RepositoryObject>> _ObjectsRepository;
        public event CollectionChangeEventHandler ObjectsRepositoryChanged;
        #endregion
    }// End Class ObjectsRepository

    public class RepositoryObject
    {
        public enum eMode { Consultation, Create, Edit };

        public RepositoryObject(object obj, eMode currentMode) 
        {
            this.m_TheObject = obj;
            this.m_CurrentMode = currentMode;
        }

        public object TheObject
        {
            get { return m_TheObject; }
            set { m_TheObject = value; }
        }

        public IServicePage FirstDetailService
        {
            get 
            {
                return m_FirstDetailServicePage;
            }
        }

        public List<IServicePage> Services
        {
            get
            {
                if (this.m_Services == null)
                    this.m_Services = new List<IServicePage>();
                return this.m_Services;
            }
        }

        public List<object> Lists
        {
            get
            {
                if (this.m_Lists == null)
                    this.m_Lists = new List<object>();
                return this.m_Lists;
            }
        }

        public int CountServices
        {
            get { return this.Services.Count; }
        }

        public int CountLists
        {
            get { return this.Lists.Count; }
        }


        public eMode CurrentMode
        {
            get { return m_CurrentMode; }
            set { m_CurrentMode = value; }
        }

        public bool AddListServicePage(IServicePage service)
        {
            if (this.Services.Contains(service) == true) return false;
            this.m_Services.Add(service);
            return true;
        }

        public bool AddDetailServicePage(IServicePage service)
        {

            if (this.Services.Contains(service) == true) return false;
            this.Services.Add(service);

            if (m_FirstDetailServicePage == null)
                m_FirstDetailServicePage = service;
            return true;
        }

        public bool RemoveServicePage(IServicePage service)
        {
            if (this.Services.Contains(service) == false) return false;
            this.Services.Remove(service);

            if (this.m_FirstDetailServicePage == service)
                this.m_FirstDetailServicePage = null;

            return true;
        }

        public bool AddListInLists(IList list)
        {
            if (this.Lists.Contains(list) == true) return false;
            this.Lists.Add(list);
            return true;
        }

        public bool RemoveListFromLists(IList list)
        {
            if (this.Lists.Contains(list) == false) return false;
            this.Lists.Remove(list);
            return true;
        }


        #region private fields
        private object m_TheObject;
        private eMode m_CurrentMode;
        private List<IServicePage> m_Services;
        private List<object> m_Lists;
        private IServicePage m_FirstDetailServicePage;
        #endregion
    } // End Class RepositoryObject
} // End Namespace
